package common;

import constant.CommonConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.bind.annotation.PostMapping;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;


@Slf4j
@EnableConfigurationProperties(FtpProperties.class)
public class FTPUtils {


    private FtpProperties ftpProperties;
    private static String host = "8.129.175.81";
    private static String username = "yjh";
    private static String password = "youjiahu";
    private static int port = 21;
    //本地字符编码
    static String LOCAL_CHARSET = "GBK";


    /**
     * 登陆FTP并获取FTPClient对象
     * @param host     FTP主机地址
     * @param port     FTP端口
     * @param userName 登录用户名
     * @param password 登录密码
     * @return
     */
    public static FTPClient loginFTP(String host, int port, String userName, String password) {
        FTPClient ftpClient = null;
        try {
            ftpClient = new FTPClient();
            ftpClient.enterLocalPassiveMode();//开启被动模式，否则文件上传不成功，也不报错
            ftpClient.setConnectTimeout(1000*30);//设置连接超时时间
            log.info("IP:"+host+":"+port);
            ftpClient.connect(host,port);// 连接FTP服务器
            ftpClient.login(userName, password);// 登陆FTP服务器
            // 开启服务器对UTF-8的支持，如果服务器支持就用UTF-8编码，否则就使用本地编码（GBK）.
            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
                LOCAL_CHARSET = "UTF-8";
            }
            ftpClient.setControlEncoding(LOCAL_CHARSET);
            // 设置文件类型为二进制（如果从FTP下载或上传的文件是压缩文件的时候，不进行该设置可能会导致获取的压缩文件解压失败）
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                log.info("连接FTP失败，用户名或密码错误。");
                ftpClient.disconnect();
            } else {
                log.info("FTP连接成功!");
            }
        } catch (Exception e) {
            log.info("登陆FTP失败，请检查FTP相关配置信息是否正确！"+ e);
            return null;
        }
        return ftpClient;
    }

    /**
     * 从FTP下载文件到本地
     * @param ftpClient     已经登陆成功的FTPClient
     * @param fileName   FTP上的目标文件路径+文件名称
     * @param localFilePath 下载到本地的文件路径
     * @param servicePath  服务器的上面文件的上层路径
     */
    public static String dowFile(FTPClient ftpClient, String fileName, String localFilePath) {
        InputStream is = null;
        FileOutputStream fos = null;
        try {

            ftpClient.enterLocalPassiveMode();
            is = ftpClient.retrieveFileStream("/upload/"+fileName);// 获取ftp上的文件
            fos = new FileOutputStream(new File(localFilePath+fileName));
            // 文件读取方式一
            int i;
            byte[] bytes = new byte[1024];
            while ((i = is.read(bytes)) != -1) {
                fos.write(bytes, 0, i);
            }
            // 文件读取方式二
            //ftpClient.retrieveFile(ftpFilePath, new FileOutputStream(new File(localFilePath)));
            ftpClient.completePendingCommand();
            log.info("FTP文件下载成功！");
        } catch (Exception e) {
            log.error("FTP文件下载失败！" + e);
        } finally {
            try {
                if (fos != null) fos.close();
                if (is != null) is.close();
            } catch (IOException e) {
                log.error("下载流关闭失败"+e);
                return null;
            }
        }
        return localFilePath+fileName;
    }

    /**
     * 从FTP下载文件到本地
     * @param ftpClient     已经登陆成功的FTPClient
     * @param fileName   FTP上的目标文件路径+文件名称
     * @param localFilePath 下载到本地的文件路径
     * @param servicePath  服务器的上面文件的上层路径
     */
    public static File downloadFile(FTPClient ftpClient,String servicePath, String fileName, String localFilePath) {
        String name = dowFile(ftpClient,fileName,localFilePath);
        if(name != null && !name.equals(""))  return new File(fileName);
        else  return null;
    }



    /**
     * 上传文件
     * @param serviceDec ftp服务保存地址
     * @param fileName 上传到ftp的文件名
     *  @param originfilename 待上传文件的名称（绝对地址） *
     * @return
     */
    public static boolean uploadFile(FTPClient ftpClient, String serviceDec, String fileName,String originfilename){
        log.info("开始上传文件");
        try(InputStream input =  new FileInputStream(new File(originfilename))) {
            return uploadFile(ftpClient,serviceDec,fileName,input,originfilename);
        } catch (FileNotFoundException e) {
            log.error("文件上传失败"+e);
        } catch (IOException e) {
            log.error("文件上传失败"+e);
        }
        return false;
    }
    /**
     * 上传文件
     * @param serviceDec ftp服务保存地址
     * @param fileName 上传到ftp的文件名
     * @param inputStream 输入文件流
     * @return
     */
    public static boolean uploadFile(FTPClient ftpClient, String serviceDec, String fileName,InputStream inputStream,String workingDirectory){
        try{
            log.info("开始上传文件");
            ftpClient.enterLocalPassiveMode();
            ftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);
            createDirecroty(ftpClient,serviceDec);
            ftpClient.makeDirectory(serviceDec);
            ftpClient.changeWorkingDirectory(workingDirectory);
            ftpClient.storeFile(fileName, inputStream);
            log.info(ftpClient.getSendDataSocketBufferSize()+"");
            inputStream.close();
            ftpClient.logout();
            log.info("上传文件成功");
        }catch (Exception e) {
            log.error("上传文件失败"+e);
        }finally{
            try {
                if(ftpClient.isConnected())
                    ftpClient.disconnect();
                if(null != inputStream)
                    inputStream.close();
            } catch (IOException e) {
                log.error("上传文件失败"+e);
                return false;
            }
        }
        return true;
    }


    //改变目录路径
    private static boolean changeWorkingDirectory(FTPClient ftpClient,String directory) {
        boolean flag = true;
        try {
            flag = ftpClient.changeWorkingDirectory(directory);
            if (flag) {
                log.info("进入文件夹" + directory + " 成功！");

            } else {
                log.info("进入文件夹" + directory + " 失败！开始创建文件夹");
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return flag;
    }

    //创建多层目录文件，如果有ftp服务器已存在该文件，则不创建，如果无，则创建
    private static boolean createDirecroty(FTPClient ftpClient,String remote) throws IOException {
        boolean success = true;
        String directory = remote + "/";
        // 如果远程目录不存在，则递归创建远程服务器目录
        if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(ftpClient,new String(directory))) {
            int start = 0;
            int end = 0;
            if (directory.startsWith("/")) {
                start = 1;
            } else {
                start = 0;
            }
            end = directory.indexOf("/", start);
            String path = "";
            String paths = "";
            while (true) {
                String subDirectory = new String(remote.substring(start, end).getBytes("GBK"), "iso-8859-1");
                path = path + "/" + subDirectory;
                if (!existFile(ftpClient,path)) {
                    if (makeDirectory(ftpClient,subDirectory)) {
                        changeWorkingDirectory(ftpClient,subDirectory);
                    } else {
                        log.info("创建目录[" + subDirectory + "]失败");
                        changeWorkingDirectory(ftpClient,subDirectory);
                    }
                } else {
                    changeWorkingDirectory(ftpClient,subDirectory);
                }

                paths = paths + "/" + subDirectory;
                start = end + 1;
                end = directory.indexOf("/", start);
                // 检查所有目录是否创建完毕
                if (end <= start) {
                    break;
                }
            }
        }
        return success;
    }

    //判断ftp服务器文件是否存在
    private static boolean existFile(FTPClient ftpClient,String path) throws IOException {
        boolean flag = false;
        FTPFile[] ftpFileArr = ftpClient.listFiles(path);
        if (ftpFileArr.length > 0) {
            flag = true;
        }
        return flag;
    }
    //创建目录
    private static boolean makeDirectory(FTPClient ftpClient,String dir) {
        boolean flag = true;
        try {
            flag = ftpClient.makeDirectory(dir);
            if (flag) {
                log.info("创建文件夹" + dir + " 成功！");

            } else {
                log.info("创建文件夹" + dir + " 失败！");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 获取FTP某一特定目录下的所有文件名称
     * @param ftpClient     已经登陆成功的FTPClient
     * @param ftpDirPath    FTP上的目标文件路径
     */
    public static List<String> getFileNameList(FTPClient ftpClient, String ftpDirPath) {
        List<String> list = new ArrayList();
        try {
            if (ftpDirPath.startsWith("/") && ftpDirPath.endsWith("/")) {
                // 通过提供的文件路径获取FTPFile对象列表
                FTPFile[] files = ftpClient.listFiles(ftpDirPath);
                // 遍历文件列表，打印出文件名称
                for (int i = 0; i < files.length; i++) {
                    FTPFile ftpFile = files[i];
                    // 此处只打印文件，未遍历子目录（如果需要遍历，加上递归逻辑即可）
                    if (ftpFile.isFile()) {
//                        log.info(ftpDirPath + ftpFile.getName());
                        list.add(ftpFile.getName());
                    }
                }
                log.info("当前FTP路径可用");
            } else {
                log.info("当前FTP路径不可用");
            }
        } catch (IOException e) {
            log.error("错误"+e);
        }
        return list;
    }

    /**
     * 获取到服务器文件夹里面最新创建的文件名称
     * @param ftpDirPath 文件路径
     * @param ftpClient ftp的连接
     * @return fileName
     */
    public static String getNewFile(FTPClient ftpClient, String ftpDirPath) throws Exception {
        if (ftpDirPath.startsWith("/") && ftpDirPath.endsWith("/")) {
            // 通过提供的文件路径获取FTPFile对象列表
            FTPFile[] files = ftpClient.listFiles(ftpDirPath);
            if(files == null)throw new Exception("文件数组为空");
            Arrays.sort(files, new Comparator<FTPFile>() {
                public int compare(FTPFile f1, FTPFile f2) {
                    return f1.getTimestamp().compareTo(f2.getTimestamp());
                }
                public boolean equals(Object obj) {
                    return true;
                }
            });
            return ftpDirPath+"/"+files[files.length-1].getName();
        }else throw new Exception("文件夹路径错误！");
    }



    /**
     * Description: 向FTP服务器上传文件
     * @param host FTP服务器hostname
     * @param port FTP服务器端口
     * @param username FTP登录账号
     * @param password FTP登录密码
     * @param basePath FTP服务器基础目录
     * @param filePath FTP服务器文件存放路径。例如分日期存放：/2019/01/01。文件的路径为basePath+filePath
     * @param filename 上传到FTP服务器上的文件名
     * @param input 输入流
     * @return 成功返回true，否则返回false
     */
    /**
     * 上传文件
     * @param
     * @return R
     */
    public static boolean uploadFile(String host, int port, String username, String password, String basePath, String filePath, String filename, InputStream input){
        boolean result = false;
        FTPClient ftp = new FTPClient();
        ftp.enterLocalPassiveMode();
        FTPClientConfig config = new FTPClientConfig();
        config.setLenientFutureDates(true);
        ftp.configure(config);
        try{
            int reply;
            log.info(ftp.getPassivePort()+"-----------------"+ftp);
            ftp.connect(host, port);    //连接FTP服务器
            //如果采用默认端口，则可以使用connect(host)的方式直接连接ftp服务器
            ftp.login(username, password);      //登录
            reply = ftp.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)){
                log.info(ftp.getPassivePort()+"-----------------"+ftp);
                ftp.disconnect();
                return result;
            }
            //切换到上传目录
            if (!ftp.changeWorkingDirectory(basePath+filePath)){
                //如果目录不存在则创建目录
                String[] dirs = filePath.split("/");
                String tempPath = basePath;
                for (String dir : dirs){
                    if (null == dir || "".equals(dir)) continue;
                    tempPath += "/" + dir;
                    if (!ftp.changeWorkingDirectory(tempPath)){
                        if (!ftp.makeDirectory(tempPath)){
                            return result;
                        }else {
                            ftp.changeWorkingDirectory(tempPath);
                        }
                    }
                }
            }
            //设置文件上传类型为二进制类型
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            //上传文件
            if (!ftp.storeFile(filename, input)){
                return result;
            }
            System.out.println(result);
            input.close();
            ftp.logout();
            result = true;
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if (ftp.isConnected()){
                try{
                    ftp.disconnect();
                }catch (IOException ioe){
                    ioe.printStackTrace();
                }
            }
        }

        return result;
    }


}

